package com.hmall.trade.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.CartClient;
import com.hmall.api.client.ItemClient;
import com.hmall.api.client.PayClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.constants.MqConstants;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.UserContext;

import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2023-05-05
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

//    private final IItemService itemService;
    private final IOrderDetailService detailService;
//    private final ICartService cartService;
    private final ItemClient itemClient;

    private final CartClient cartClient;

    private final RabbitTemplate rabbitTemplate;

    private final PayClient payClient;

    /**
     * 创建订单
     * @param orderFormDTO
     * @return
     */
    @Override
//    @Transactional
    @GlobalTransactional //seata中标记事务的入口
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.订单数据
        Order order = new Order();
        // 1.1.查询商品
        List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
        // 1.2.获取商品id和数量的Map
        Map<Long, Integer> itemNumMap = detailDTOS.stream()
                .collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
        Set<Long> itemIds = itemNumMap.keySet();
        // 1.3.查询商品
//        List<ItemDTO> items = itemService.queryItemByIds(itemIds);
        List<ItemDTO> items = itemClient.queryByItemIds(itemIds);
        if (items == null || items.size() < itemIds.size()) {
            throw new BadRequestException("商品不存在");
        }
        // 1.4.基于商品价格、购买数量计算商品总价：totalFee
        int total = 0;
        for (ItemDTO item : items) {
            total += item.getPrice() * itemNumMap.get(item.getId());
        }
        order.setTotalFee(total);
        // 1.5.其它属性
        order.setPaymentType(orderFormDTO.getPaymentType());
        order.setUserId(UserContext.getUser());
        order.setStatus(1);
        // 1.6.将Order写入数据库order表中
        save(order);

        // 2.保存订单详情
        List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
        detailService.saveBatch(details);

        // 3.清理购物车商品
//        cartService.removeByItemIds(itemIds);
//        cartClient.deleteCartItemByIds(itemIds);

        //TODO 使用MQ完成清理购物车的业务
        try {
            rabbitTemplate.convertAndSend("trade.topic", "order.create", itemIds, new MessagePostProcessor() {
                @Override
                //为该消息添加一个userId的头
                public Message postProcessMessage(Message message) throws AmqpException {
                    message.getMessageProperties().setHeader("userId", order.getUserId());
                    return message;
                }
            });
        } catch (AmqpException e) {
            log.error("清空当前用户："+order.getUserId()+"的购物车失败");
        }

        // 4.扣减库存
        try {
//            itemService.deductStock(detailDTOS);
            itemClient.deductStock(detailDTOS);
        } catch (Exception e) {
            throw new RuntimeException("库存不足！");
        }

        //TODO 超时订单问题
        //5.使用延迟消息解决消息未收到
        // 为了保持数据一致性，当订单创建15分钟后，订单的状态的一直都是处于未付款状态
        // 此时发送一条消息，消息体是订单的id
        rabbitTemplate.convertAndSend(MqConstants.DELAY_EXCHANGE_NAME, MqConstants.DELAY_ORDER_KEY, order.getId(), new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setDelay(20000);  //延迟20秒钟
                return message;
            }
        });

        return order.getId();
    }

    /**
     * 修改订单状态
     * @param orderId
     */
    @Override

    public void markOrderPaySuccess(Long orderId) {
        Order order = new Order();
        System.out.println("开始修改当前订单:" + orderId+ "的状态");
        order.setId(orderId);
        order.setStatus(2);
        order.setPayTime(LocalDateTime.now());
        updateById(order);
    }

    /**
     * 取消订单，并且恢复库存
     * @param orderId
     */
    @Override
    @Transactional
    public void cancelOrder(Long orderId) {
        System.out.println("订单已取消");
        log.info("准备修改当前订单：{}的状态",orderId);
       //1.修改订单的状态为取消订单
        lambdaUpdate().set(Order::getStatus,5)
                .eq(Order::getId,orderId)
                .update();

        //2.修改支付单的状态
        log.info("准备修改当前订单关联的支付单状态");
        //调用PayClient，完成对支付单状态的修改
        payClient.updateByBizOrderNo(orderId,2);

        //3.恢复库存
        log.info("准备恢复商品的库存");
        //1.根据订单id查询关联的orderDetail详情
        List<OrderDetail> orderDetails = detailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        if (!orderDetails.isEmpty()){
            List<OrderDetailDTO> detailDTOS = BeanUtils.copyList(orderDetails, OrderDetailDTO.class);
            //调用CartClient进行库存恢复
            itemClient.restoreStock(detailDTOS);
        }



    }

    private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
        List<OrderDetail> details = new ArrayList<>(items.size());
        for (ItemDTO item : items) {
            OrderDetail detail = new OrderDetail();
            detail.setName(item.getName());
            detail.setSpec(item.getSpec());
            detail.setPrice(item.getPrice());
            detail.setNum(numMap.get(item.getId()));
            detail.setItemId(item.getId());
            detail.setImage(item.getImage());
            detail.setOrderId(orderId);
            details.add(detail);
        }
        return details;
    }
}
